package geppetto.cat.alignments;


import geppetto.cat.alignments.output.AlignerOutput;
import geppetto.cat.common.Pair;
import geppetto.cat.corpus.BilingualCorpus;
import geppetto.cat.programs.ComparePredictedAligmentSets.DiffRes;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.zip.GZIPInputStream;

import javax.management.RuntimeErrorException;




public class AlignmentsSet {
	public ArrayList<Alignment> _alignments;

	public AlignmentsSet() {
		_alignments = new ArrayList<Alignment>();
	}

	public AlignmentsSet(ArrayList<Alignment> alignments) {
		_alignments = alignments;
	}

	/*
	 * public AlignmentsSet(String file, byte sentenceSource, BilingualCorpus
	 * corpus) throws IOException{ this(file, sentenceSource,corpus,0); }
	 */
	
	protected static BufferedReader getAlignmentFileReader(String file) throws FileNotFoundException, IOException{
		BufferedReader input = null;
		if(new File(file+".gz").exists()){
			input = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(file+".gz"))));
		}
		else{
			if(new File(file).exists()){
				if(file.endsWith(".gz")){
					input = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(file))));					
				}
				else{
					input = new BufferedReader(new InputStreamReader(
						new FileInputStream(file)));
				}
			}
		}
		return input;
	}

	public static AlignmentsSet getAlignmentFromMoses(String file,
			byte sentenceSource, BilingualCorpus corpus) throws IOException {
		AlignmentsSet set = new AlignmentsSet();
		set._alignments = new ArrayList<Alignment>();
		int sentenceNr = 0;
		BufferedReader input = getAlignmentFileReader(file);
		if(input == null){
			throw new IOException("could not find alignments file");
		}
		while ((input.ready()) && sentenceNr < corpus.getNumSentences(sentenceSource)) {
			Alignment gold = readMosesAlignment(input, sentenceNr, sentenceSource, 
					corpus.getSourceSentenceLength(sentenceNr, sentenceSource), 
					corpus.getForeignSentenceLength(sentenceNr, sentenceSource));			
			set.addAlignment(gold);
			sentenceNr++;
		}
		System.out.println("Finish Building Alignments Set");
		return set;
	}
	
	protected static Alignment readMosesAlignment(BufferedReader input, int sentenceNr, byte sentenceSource, int sourceLen, int targetLen) throws IOException{
		String line = input.readLine();
		if(line == null){
			return null;
		}
		if(!line.matches("(([0-9]+\\-[0-9]+)|([0-9]+\\-[0-9]+\\s))+")){
			System.err.println("Warning: line " + sentenceNr + " is empty or does not match the specified format, skipping...");
			return null;
		}
		Alignment al = new Alignment(sentenceNr, sentenceSource, sourceLen, targetLen);
		String[] fields = line.split("\\s+");
		for (int i = 0; i < fields.length; i++) {
			String pos[] = fields[i].split("-");
			al.add(Integer.parseInt(pos[0]), Integer.parseInt(pos[1]));
		}
		return al;
	}

	public static AlignmentsSet getAlignmentFromPosterior(String file,
			byte sentenceSource, BilingualCorpus corpus) throws IOException {
		AlignmentsSet set = new AlignmentsSet();
		set._alignments = new ArrayList<Alignment>();
		int sentenceNr = 0;
		BufferedReader input = getAlignmentFileReader(file);
		if(input == null){
			throw new IOException("could not find alignments file");
		}
		while(input.ready() && sentenceNr < corpus.getNumSentences(sentenceSource)){
			int sourceLen = corpus.getSourceSentenceLength(sentenceNr, sentenceSource);
			int foreignLen = corpus.getForeignSentenceLength(sentenceNr, sentenceSource);
			Alignment gold = readPosteriorAlignment(input, sentenceNr, sentenceSource ,sourceLen, foreignLen);			
			sentenceNr++;
			set.addAlignment(gold);
		}
		System.out.println("Finish Building Alignments Set");
		return set;
	}

	
	public static Alignment readPosteriorAlignment(BufferedReader input, int sentenceNr, byte sentenceSource, int sourceLen, int targetLen) throws IOException{
		String line = null;
		line = input.readLine();
		while(line.equals("") || line.startsWith("s:")){
			line = input.readLine();
		}
		if(line == null){
			return null;
		}
		Alignment al = new Alignment(sentenceNr, sentenceSource,sourceLen,targetLen);
		for (int foreignPos = 0; foreignPos < targetLen; foreignPos++) {
			String[] fields = line.split("\\s+");
			if(fields.length != sourceLen){
				System.out.println("Error reading from posterior format");
				System.out.println("Sentence: " + sentenceNr + "-" + sentenceSource + " wrong size");
				System.out.println("got: " + fields.length + " instead of " + sourceLen);
				throw new UnsupportedOperationException();
			}
			for (int sourcePos = 0; sourcePos < sourceLen; sourcePos++) {
				String[] entries = fields[sourcePos].split(":");
				double posterior = Double.parseDouble(entries[0]);
				al.addPosterior(sourcePos, foreignPos,posterior);
				if(entries[1].equalsIgnoreCase("P")){
					al.add(sourcePos,foreignPos);
				}
			}
			line = input.readLine();
		}
		return al;
	}

	public static AlignmentsSet getAlignmentFromFileNAACL(String file,
			byte sentenceSource, BilingualCorpus corpus,
			int initialSentenceNumber) throws IOException {
		AlignmentsSet set = new AlignmentsSet();
		set._alignments = new ArrayList<Alignment>();
		Alignment gold = null;
		int sentenceNumber = initialSentenceNumber;
		int number;
		BufferedReader input = new BufferedReader(new InputStreamReader(
				new FileInputStream(file)));
		String line = input.readLine();
		while ((line != null)) {

			String[] fields = line.split("\\s+");
			number = Integer.parseInt(fields[0]);
			// System.out.println(corpus.getSourceSentenceString(number,
			// sentenceSource));
			// System.out.println(corpus.getForeignSentenceString(number,
			// sentenceSource));

			// System.out.println(number +" " + sentenceNumber);
			if (number < sentenceNumber) {
				// System.out.println("Ignoring alignmnet for sentence " +
				// number);
				line = input.readLine();
				continue; // we don't want to see this
			}
			if (number != sentenceNumber) {
				// System.out.println("Reading new line");
				if (gold != null) {
					set._alignments.add(gold);
				}
				sentenceNumber = number;
				// System.out.println("SentenceNumber" + (sentenceNumber-1) + "
				// " + corpus.getSourceSentenceLength(sentenceNumber-1 ,
				// sentenceSource)+ " " +
				// corpus.getForeignSentenceLength(sentenceNumber-1,
				// sentenceSource));
				gold = new Alignment(sentenceNumber - 1, sentenceSource, corpus
						.getSourceSentenceLength(sentenceNumber - 1,
								sentenceSource), corpus
								.getForeignSentenceLength(sentenceNumber - 1,
										sentenceSource));
			}
			if (gold == null) {
				gold = new Alignment(sentenceNumber - 1, sentenceSource, corpus
						.getSourceSentenceLength(sentenceNumber - 1,
								sentenceSource), corpus
								.getForeignSentenceLength(sentenceNumber - 1,
										sentenceSource));
			}
			int sindex = Integer.parseInt(fields[1]);
			int findex = Integer.parseInt(fields[2]);
			byte type;
			// If there is no field aligment is Sure (according to EPPS)
			if (fields.length == 4 && fields[3].equals("P"))
				type = 1;
			else
				type = 2;
			// System.out.print(corpus.getSourceWord((sentenceNumber-1),
			// sentenceSource, sindex-1) +" ");
			// System.out.println(corpus.getForeignWord((sentenceNumber-1),
			// sentenceSource, findex-1) + "\n");
			// System.out.println((sentenceNumber-1) + "sIndex " + sindex + "
			// fIndex " + findex +" type " + type);
			gold.add(sindex - 1, findex - 1, type);
			line = input.readLine();
		}
		set.addAlignment(gold); // Catch last alignment
		System.out.println("Finish Building Alignments Set");
		return set;
	}

	public void addAlignment(Alignment al) {
		_alignments.add(al);
	}

	public Alignment get(int number) {
		return _alignments.get(number);
	}

	public AlignmentsSet reverse() {
		AlignmentsSet reversed = new AlignmentsSet();
		ArrayList<Alignment> goldsR = new ArrayList<Alignment>();
		for (Alignment gold : _alignments) {
			goldsR.add(gold.reverse());
		}
		reversed._alignments = goldsR;
		return reversed;
	}

	public int size() {
		return _alignments.size();
	}

	public ArrayList<Alignment> getAlignments() {
		return _alignments;
	}

	public Alignment getAlignmentByPos(int pos) {
		return _alignments.get(pos);
	}

	/*
	 * public HashMap<Integer,Float> nullAlignedBySentenceLenght(){ HashMap<Integer,Float>
	 * nullAligned = new HashMap<Integer,Float>(); HashMap<Integer,Integer>
	 * nrSentences = new HashMap<Integer,Integer>(); for(Alignment gold:
	 * _alignments){ int lenght = gold.getForeignLenght(); float nrNull =
	 * gold.getNumberNulls(); if(nullAligned.containsKey(lenght)){ nrNull +=
	 * nullAligned.get(lenght);
	 * nrSentences.put(lenght,1+nrSentences.get(lenght)); }else{
	 * nrSentences.put(lenght,1); } nullAligned.put(lenght, nrNull); }
	 * for(Integer i: nullAligned.keySet()){ nullAligned.put(i,
	 * nullAligned.get(i)/nrSentences.get(i)); } return nullAligned; }
	 */

	public void outputWithStatistics(BilingualCorpus corpus, PrintStream out,
			int maxNumber) throws UnsupportedEncodingException {
		ArrayList<Pair<Integer, Integer>> mask = new ArrayList<Pair<Integer, Integer>>();
		mask.add(new Pair<Integer, Integer>(-1, -1));
		mask.add(new Pair<Integer, Integer>(-1, 0));
		mask.add(new Pair<Integer, Integer>(0, -1));
		mask.add(new Pair<Integer, Integer>(0, 1));
		mask.add(new Pair<Integer, Integer>(1, 0));
		mask.add(new Pair<Integer, Integer>(1, 1));
		mask.add(new Pair<Integer, Integer>(-1, 1));
		mask.add(new Pair<Integer, Integer>(1, -1));

		double numberalignedPoints = 0;
		double numberSurePoints = 0;
		double numberPossiblePoints = 0;
		double distanceToDiagonal = 0;
		double meanFertility = 0;

		int i = 1;

		for (Alignment a : getAlignments()) {
			if (i <= maxNumber) {
				out.println("Gold Alignments " + i);
				AlignerOutput.outputGold(a, corpus, out);
				out.println(" Source Len" + a.getSourceLenght()
						+ " Foreign len " + a.getForeignLenght());
				int nAlignedPoints = a.numberOfAlignedPoints();
				int nSureAligedPoints = a.numberOfAlignedPoints(2);
				int nPossibleAligedPoints = a.numberOfAlignedPoints(1);
				numberalignedPoints += nAlignedPoints;
				numberSurePoints += nSureAligedPoints;
				numberPossiblePoints += nPossibleAligedPoints;
				distanceToDiagonal += a.distanceToDiagonal();
				meanFertility += a.meanFertility();
				out.println(" number of aligned points: " + nAlignedPoints
						+ " %sure " + nSureAligedPoints * 1.0 / nAlignedPoints
						+ " % possible " + nPossibleAligedPoints * 1.0
						/ nAlignedPoints);
				out.println(" number of lonely points: "
						+ a.numberOfLonelyPoints(mask));
				out.println(" nmean fertility: " + a.meanFertility());
				out.println(" distance to diagonal: " + a.distanceToDiagonal());
				out.println();
				out.println();
				i++;
			}
		}
		out.println("TOTAL CORPUS");
		out.println(" nmean fertility: " + meanFertility
				/ getAlignments().size());
		out.println(" number of aligned points: " + numberalignedPoints
				+ " %sure " + numberSurePoints * 1.0 / numberalignedPoints
				+ " % possible " + numberPossiblePoints * 1.0
				/ numberalignedPoints);
		out.println(" distance to diagonal: " + distanceToDiagonal / i);
	}

	public void outputWithStatistics(BilingualCorpus corpus, PrintStream out)
	throws UnsupportedEncodingException {
		outputWithStatistics(corpus, out, Integer.MAX_VALUE);
	}




	public int numberOfAlignedPoints() {
		int number = 0;
		for (Alignment al : _alignments) {
			number += al.numberOfAlignedPoints();
		}
		return number;
	}

	public int numberOfAlignedPoints(int type) {
		int number = 0;
		for (Alignment al : _alignments) {
			number += al.numberOfAlignedPoints(type);
		}
		return number;
	}

	public int numberOfSourceUnaligned() {
		int number = 0;
		for (Alignment al : _alignments) {
			number += al.getNumberOfSourceNotAligned();
		}
		return number;
	}

	public AlignmentsSet[] splitBySentenceLen() {
		int nrBins = 5;
		AlignmentsSet[] sets = new AlignmentsSet[nrBins];
		for (int i = 0; i < nrBins; i++) {
			sets[i] = new AlignmentsSet();
		}
		for (int i = 0; i < _alignments.size(); i++) {
			Alignment al = _alignments.get(i);
			if (al._foreignLen < 10) {
				sets[0].addAlignment(al);
			} else if (al._foreignLen < 20) {
				sets[1].addAlignment(al);
			} else if (al._foreignLen < 30) {
				sets[2].addAlignment(al);
			} else if (al._foreignLen < 40) {
				sets[3].addAlignment(al);
			} else {
				sets[4].addAlignment(al);
			}
		}
		return sets;
	}

	/**
	 * Sorts the alignments from worst to better according to AER
	 * @return
	 */
	public AlignmentsSet sortByAER(AlignmentsSet setGold){
		AlignmentsSet sorted = new AlignmentsSet();
		ArrayList<Pair<Alignment,Double>> tosort = new ArrayList<Pair<Alignment,Double>>();
		for(int i = 0; i < _alignments.size();i++){
			Alignment a = _alignments.get(i);
			Alignment gold = setGold.get(i);
			double aer = AlignmentEvaluator.evaluate(a, gold).getAER();
			tosort.add(new Pair(a,aer));
		}
		Collections.sort(tosort,new AlignmentAERComparator());
		for(int i = 0; i < _alignments.size();i++){
			sorted.addAlignment(tosort.get(i).first());
		}
		return sorted;
	}

	//Should go to some commone place
	public static class AlignmentAERComparator implements Comparator<Pair<Alignment,Double>>{

		public int compare(Pair<Alignment,Double> o1, Pair<Alignment,Double> o2) {
			if(o1.second() < o2.second())
				return 1;
			else if (o1.second() > o2.second()){
				return -1;
			}else{
				return 0;
			}

		}

	}

	public static void main(String[] args) throws IOException {

		BilingualCorpus corpus = new BilingualCorpus(10000, "test", "-", "-");
		corpus.addTestFile(args[0], args[1]);
		corpus.addGoldAligments(args[2]);
		AlignmentsSet g = corpus.getGold();
		g.outputWithStatistics(corpus, System.out, 100);

		/*
		 * "/scratch/graca/mwa/Manual-Alignemts/working-corpus/100.en"
		 * "/scratch/graca/mwa/Manual-Alignemts/working-corpus/100.es"
		 * "/scratch/graca/mwa/Manual-Alignemts/working-corpus/100.pt"
		 * "/scratch/graca/mwa/Manual-Alignemts/working-corpus/100.fr"
		 * 
		 * "/scratch/graca/mwa/Manual-Alignemts/working-corpus/e-es/e-es-100.wa"
		 * "/scratch/graca/mwa/Manual-Alignemts/working-corpus/e-pt/e-pt-100.wa"
		 * "/scratch/graca/mwa/Manual-Alignemts/working-corpus/e-fr/e-fr-100.wa"
		 * "/scratch/graca/mwa/Manual-Alignemts/working-corpus/pt-es/pt-es-100.wa"
		 * "/scratch/graca/mwa/Manual-Alignemts/working-corpus/pt-fr/pt-fr-100.wa"
		 * "/scratch/graca/mwa/Manual-Alignemts/working-corpus/es-fr/es-fr-100.wa"
		 */

		// corpus.addTestFile("/scratch/graca/data/naacl2003/English-French/test/test-final-347.e","/scratch/graca/data/naacl2003/English-French/test/test-final-347.f");

		// corpus.addGoldAligments("/Users/javg/Projects/Word Alignments
		// Algorithms/hansardslx/test/test.wa.nonullalign-347");
		// corpus.addGoldAligments("/scratch/graca/data/naacl2003/English-French/answers/test.wa.nonullalign-347");

		/*
		 * Corpus source = new Corpus(Corpora.EPPS_EUROPARL_ENGLISH_TEST_FILE);
		 * Corpus foreign = new Corpus(Corpora.EPPS_EUROPARL_SPANISH_TEST_FILE);
		 * GoldAlignmentsSet set = new
		 * GoldAlignmentsSet(Corpora.EPPS_ENGLISH_SPANISH_GOLD_ALIGMENTS,source,foreign);
		 *  /* Corpus source = new Corpus(Corpora.HANSARDS_ENGLISH_TEST_FILE);
		 * Corpus foreign = new Corpus(Corpora.HANSARDS_FRENCH_TEST_FILE);
		 * GoldAlignmentsSet set = new
		 * GoldAlignmentsSet(Corpora.HANSARDS_ENGLISH_FRENCH_GOLD_ALIGMENTS,source,foreign);
		 */
		/*
		 * HashMap<Integer,Float> nulls = set.nullAlignedBySentenceLenght();
		 * for(Integer i: nulls.keySet()){ System.out.println("sentence Lenght " +
		 * i + " NR null " + nulls.get(i)); }
		 */
		/*
		 * AlignerOutput out = new AlignerOutput(); for(GoldAlignment g:
		 * set.getAlignments()){ out.outputGold(g, System.out); }
		 */

		/*
		 * GoldAlignment g = set.getAlignments().get(0); //for(GoldAlignment g:
		 * set.getAlignments()){ out.outputGold(g, System.out); //}
		 */

	}
}
